/*
 * ArbolDeHuffman.cpp
 *
 *  Created on: 09/06/2014
 *      Author: manuel
 */

#include "Huffman.h"

Huffman::Huffman() {
	subfijo_cod = ".cod.HUFF";
	subfijo_decod = ".decod.HUFF";
}

Huffman::~Huffman() {}

void emitirCaracter( Byte& byteDecod, Bloque& bqDecod, Archivo& decod ) {

	if( !bqDecod.escribirByte(byteDecod) )
	{
		decod.escribirBloque(bqDecod);
		bqDecod.iniciar();
		bqDecod.escribirByte(byteDecod);
	}
}

bool sacarBit( std::string& direccion, Byte& byteComp) {

	if( !byteComp.pop_bits(direccion, 1) )
		return false;
	return true;
}

unsigned int obtenerTamano(Archivo &comprimido) {
	Bloque bqInt(sizeof(unsigned int));
	unsigned int aux;

	comprimido.leerBloque(bqInt);

	memcpy( (unsigned char*)&aux, bqInt.getData(), sizeof(unsigned int) );

	printf("origi tamano: %d \n", aux);

	return aux;
}


void meterBits(std::string& codif, Byte& byteComp, Bloque& bqComp, Archivo& comprimido) {

	unsigned short j;
	std::string bit;

	for( j = 0; j < codif.size(); j++)
	{
		bit = codif[j];
		if( !byteComp.push_bits(bit) )
		{
			if( !bqComp.escribirByte(byteComp) )
			{
				comprimido.escribirBloque(bqComp);
				bqComp.iniciar();
				bqComp.escribirByte(byteComp);
			}
			byteComp.resetear();
			byteComp.push_bits(bit);
		}
	}
}

void Huffman::actualizarTabla(Tabla& tabla, unsigned char caracter){
	Tabla::iterator it;
	HuffNodo *actualizado;
	unsigned int freqActual;

	// si el caracter es 0, no hay q actualizar nada
	if ( caracter == 0 )
		return;

	for( it = tabla.begin(); it->second->getCaracter() != caracter; it++)
		;

		freqActual = it->first;
		tabla.erase(it);
		freqActual++;
		actualizado = new HuffNodo(freqActual, caracter, NULL, NULL);
		tabla.insert(Tabla::value_type(actualizado->getFreq(), actualizado));
}

void emitirTamano(Archivo &MTF, Archivo &comprimido) {
	Bloque bqInt(sizeof(unsigned int));
	unsigned int tam;
	tam = MTF.getTamano();

	printf("tamano archivo MTF: %d tam: %d \n", tam, (unsigned int)sizeof(unsigned int));
	if( !bqInt.setData((unsigned char*)&tam, sizeof(unsigned int)) )
		printf("NO SE EMITIO EL TAM\n");

	comprimido.escribirBloque(bqInt);
}

void Huffman::crearTablaInicial(Tabla &tabla) {
	HuffNodo *nodoNuevo;
	unsigned int i;

	for ( i = 0; i < 256 ; i++ ){
			nodoNuevo = new HuffNodo(1, (unsigned char)i, NULL, NULL);
			tabla.insert(Tabla::value_type( 1,  nodoNuevo));
	}
}

void Huffman::crearTablaInicialOptimizada(Tabla &tabla) {
	HuffNodo *nodoNuevo;
	unsigned int i;

	// excluyo el 0
	for ( i = 1; i < 256 ; i++ ){
			nodoNuevo = new HuffNodo(1, (unsigned char)i, NULL, NULL);
			tabla.insert(Tabla::value_type( 1,  nodoNuevo));
	}
}

std::string Huffman::decodificarDinamico(std::string pt, unsigned int tam_bloque){
	std::string nombre_decod = pt.substr(0, pt.size()-subfijo_cod.size());
	nombre_decod +=  subfijo_decod;

	Archivo comprimido(pt); comprimido.abrir();
	Bloque bqComp(tam_bloque);
	Byte byteComp;

	Archivo decod(nombre_decod); decod.abrir();
	Bloque bqDecod(tam_bloque);
	Byte byteDecod;

	// creamos la tabla de freqs inicial, con todas las freqs = 1
	Tabla tabla;
	//crearTablaInicial(tabla);
	crearTablaInicialOptimizada(tabla);

	// creamos el arbol con la tabla
	HuffmanArbol arbolCod;

	unsigned int BYTES_TOTALES, bytesEmitidos = 0;
	BYTES_TOTALES = obtenerTamano(comprimido);

	std::string codif, OchoBits, direccion;
	unsigned short i;
	bool encontrado = true;

	while( comprimido.leerBloque(bqComp) )
	{
		while( bqComp.leerByte(byteComp) )
		{
			OchoBits = byteComp.string();
			for( i = 0; i < 8; i ++)
			{
				if( encontrado )
				{
					// genero arbol
					arbolCod.setTabla(tabla);
					arbolCod.generarArbolOptimizado();

					// me muevo por el arbol segun los bits que voy leyendo, hasta llegar a una hoja
					// donde estará el caracter a emitir
					arbolCod.moverActualARaiz();
					encontrado = false;
				}

				direccion = OchoBits[i];
				encontrado = arbolCod.moverActualHastaHoja(direccion);

				if( encontrado )
				{
					byteDecod.setValor( arbolCod.getCaracterNodoActual() );

					emitirCaracter( byteDecod, bqDecod, decod ); bytesEmitidos++;

					arbolCod.destruirOptimizado();

					actualizarTabla(tabla, byteDecod.valor());
				}
			}
		}
		bqComp.iniciar();
	}

	bqComp.resize(comprimido.bytesLeidos());

	while( bqComp.leerByte(byteComp) )
	{
		OchoBits = byteComp.string();
		for( i = 0; i < 8; i ++)
		{
			if( encontrado )
			{
				// genero arbol
				arbolCod.setTabla(tabla);
				arbolCod.generarArbolOptimizado();

				// me muevo por el arbol segun los bits que voy leyendo, hasta llegar a una hoja
				// donde estará el caracter a emitir
				arbolCod.moverActualARaiz();
				encontrado = false;
			}

			direccion = OchoBits[i];
			encontrado = arbolCod.moverActualHastaHoja(direccion);

			if( encontrado )
			{
				byteDecod.setValor( arbolCod.getCaracterNodoActual() );

				if( bytesEmitidos < BYTES_TOTALES ){
					emitirCaracter( byteDecod, bqDecod, decod );
					bytesEmitidos++;
				}

				arbolCod.destruirOptimizado();

				actualizarTabla(tabla, byteDecod.valor());
			}
		}
	}
	decod.escribirBloque(bqDecod);

	// para que no se trate de eliminar un nodo ya eliminado
	arbolCod.moverActualARaiz();
	std::cout << "bytesEmitidos: " << bytesEmitidos << " BYTES_TOTALES: " << BYTES_TOTALES << '\n';

	comprimido.cerrar();
	decod.cerrar();

	return nombre_decod;
}

std::string Huffman::codificarDinamico(std::string pt, unsigned int tam_bloque){

	Archivo MTF(pt); MTF.abrir();
	Bloque bqMTF(tam_bloque);
	Byte byteMTF;

	Archivo comprimido(pt+".DINAMICO"+subfijo_cod); comprimido.abrir();
	Bloque bqComp(tam_bloque);
	Byte byteComp;

	// creamos la tabla de freqs inicial, con todas las freqs = 1
	Tabla tabla;
	crearTablaInicialOptimizada(tabla);

	// creamos el arbol con la tabla
	HuffmanArbol arbolCod;

	std::string codif;

	emitirTamano(MTF, comprimido);

	// leemos bloques hasta que se termine el archivo MTF
	while( MTF.leerBloque(bqMTF) )
	{
		// leemos bytes hasta que se termine el bqMTF
		while( bqMTF.leerByte(byteMTF) )
		{
			// genero arbol
			arbolCod.setTabla(tabla);
			arbolCod.generarArbolOptimizado();

			// recupero el codigo del caracter
			if( !arbolCod.getCodificacionChar2(byteMTF.valor(), codif) ){
				std::cout << "ERROR; NO SE ENCONTRO EL CARACTER A CODIFICAR\n"; return "FALLA";
			}
			arbolCod.destruirOptimizado();

			// meto bits al byte, y meto el byte en el bloque
			meterBits(codif, byteComp, bqComp, comprimido);

			//actualizo tabla de freq solo si salio un byte distinto al 0(cero)
			actualizarTabla(tabla, byteMTF.valor());

			codif.resize(0);
		}
		bqMTF.iniciar();
	}

	bqMTF.resize(MTF.bytesLeidos());

	unsigned short i=0;
	// ultimo bloque leido
	while( bqMTF.leerByte(byteMTF) )
	{
		// genero arbol
		arbolCod.setTabla(tabla);
		arbolCod.generarArbolOptimizado();

		// recupero el codigo del caracter y luego aumento su freq
		codif.resize(0);
		if( !arbolCod.getCodificacionChar2(byteMTF.valor(), codif) ){
			std::cout << "ERROR; NO SE ENCONTRO EL CARACTER A CODIFICAR\n"; return "FALLA";
		}
		arbolCod.destruirOptimizado();

		// meto bits al byte, y meto el byte en el bloque
		meterBits(codif, byteComp, bqComp, comprimido);

		//actualizo tabla de freq
		actualizarTabla(tabla, byteMTF.valor());
		i++;
	}
	if( byteComp.bitsLibres() != 8 )
		bqComp.escribirByte(byteComp);

	comprimido.escribirBloque(bqComp);

	MTF.cerrar();
	comprimido.cerrar();

	return pt+".DINAMICO"+subfijo_cod;
}
